﻿using System;
using System.Xml;
using System.IO;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;

using System.Diagnostics;

namespace Microsoft.BizTalk.Samples.EDIGuidance.PipelineComponents
{
    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [ComponentCategory(CategoryTypes.CATID_Any)]
    [System.Runtime.InteropServices.Guid("207E8A10-60EC-4625-A470-F81E59CE1A80")]
    public class X12AckPropertyPromotion_Component : IBaseComponent, IComponentUI, IComponent, IPersistPropertyBag
    {

        /// <summary>
        /// Copy message parts from source to destination message.
        /// </summary>
        /// <param name="sourceMessage">Source message</param>
        /// <param name="destinationMessage">Destination message</param>
        /// <param name="newBodyPart">New message body part</param>
        private void CopyMessageParts(IBaseMessage sourceMessage, IBaseMessage destinationMessage, IBaseMessagePart newBodyPart)
        {
            string bodyPartName = sourceMessage.BodyPartName;
            for (int c = 0; c < sourceMessage.PartCount; ++c)
            {
                string partName = null;
                IBaseMessagePart messagePart = sourceMessage.GetPartByIndex(c, out partName);
                if (partName != bodyPartName)
                {
                    destinationMessage.AddPart(partName, messagePart, false);
                }
                else
                {
                    destinationMessage.AddPart(bodyPartName, newBodyPart, true);
                }
            }
        }


        #region IPersistPropertyBag Members

        public void GetClassID(out Guid classID)
        {
            classID = new Guid("6611DDA2-77AF-4eea-BA30-653C09578CD2");
        }

        public void InitNew()
        {

        }

        public void Load(IPropertyBag propertyBag, int errorLog)
        {

        }

        public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
        {

        }

        #endregion

        #region IBaseComponent Members

        public string Description
        {
            get { return "Pipeline Component Used to promote properties for X12 Ack correlation."; }
        }

        public string Name
        {
            get { return "X12 Ack Property Promotion"; }
        }

        public string Version
        {
            get { return "1.0.0.0"; }
        }

        #endregion

        #region IComponentUI Members

        public IntPtr Icon
        {
            get { return new System.IntPtr(); }
        }

        public System.Collections.IEnumerator Validate(object projectSystem)
        {
            return null;
        }

        #endregion

        #region IComponent Members

        public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            // Check arguments
            if (null == pContext)
                throw new ArgumentNullException("pContext");

            if (null == pInMsg)
                throw new ArgumentNullException("pInMsg");

            if (null == pInMsg.BodyPart)
                throw new ArgumentNullException("pInMsg.BodyPart");

            if (null == pInMsg.BodyPart.GetOriginalDataStream())
                throw new ArgumentNullException("pInMsg.BodyPart.GetOriginalDataStream()");

            // Create a new output message
            IBaseMessage outMessage = pContext.GetMessageFactory().CreateMessage();

            try
            {
                //
                // 1. Create a seekable read-only stream over the input message body part stream.
                //

                // Create a virtual stream, using GetOriginalDataStream() method on the IBaseMessagePart because
                // this call doesn't clone stream (instead of IBaseMessagePart.Data property).
                SeekableReadOnlyStream stream = new SeekableReadOnlyStream(pInMsg.BodyPart.GetOriginalDataStream());

                //
                // 2. Create a new outgoing message, copy all required stuff.
                //

                // Copy message context by reference
                outMessage.Context = pInMsg.Context;

                // Create new message body part
                IBaseMessagePart newBodyPart = pContext.GetMessageFactory().CreateMessagePart();

                // Copy body part properties by references.
                newBodyPart.PartProperties = pInMsg.BodyPart.PartProperties;

                // Set virtual stream as a data stream for the new message body part
                newBodyPart.Data = stream;

                // Copy message parts
                CopyMessageParts(pInMsg, outMessage, newBodyPart);


                //
                // 3. Load document stream into XmlDocument.
                //

                // Save new message stream's current position
                long position = stream.Position;

                // Load document into XmlDocument
                XmlDocument doc = new XmlDocument();
                doc.Load(stream);

                // Restore the 0 position for the virtual stream
                Debug.Assert(stream.CanSeek);

                // Restore new message stream's current position
                stream.Position = position;

                string _inBoundDocumentType = pInMsg.Context.Read("GS01", "http://schemas.microsoft.com/Edi/PropertySchema").ToString();

                if (_inBoundDocumentType == "FA")
                {

                    //copy context properties promoted by the EDI pipeline component to my custom context properties
                    string _receiverQualifier = pInMsg.Context.Read("ISA05", "http://schemas.microsoft.com/Edi/PropertySchema").ToString();
                    string _receiverName = pInMsg.Context.Read("ISA06", "http://schemas.microsoft.com/Edi/PropertySchema").ToString();

                    //Grab the AK2 element from the 997 which correlates with the origional group control number
                    string _groupControlNumber = doc.SelectSingleNode("//AK102/text()").InnerText;
                    string _acknowledgedDocumentType = doc.SelectSingleNode("//AK101/text()").InnerText;

                    //Note, since the property is not set on the message, the property schema being used 
                    //must implement MessageContextPropertyBase or the property will not be promoted
                    pInMsg.Context.Promote("ReceiverName", "https://Microsoft.BizTalk.Samples.EDIGuidance.Schemas.PropertySchemas/v1.0", _receiverName);
                    pInMsg.Context.Promote("GroupControlNumber", "https://Microsoft.BizTalk.Samples.EDIGuidance.Schemas.PropertySchemas/v1.0", _groupControlNumber);
                    pInMsg.Context.Promote("DocumentType", "https://Microsoft.BizTalk.Samples.EDIGuidance.Schemas.PropertySchemas/v1.0", _acknowledgedDocumentType);
                }
            }
            catch (Exception e)
            {
                Debug.WriteLine(string.Format(">>> EXCEPTION! {0}", e.Message));
                throw;
            }

            return outMessage;
        }

        #endregion
    }
}
